package jpa4azure.impl;

import static jpa4azure.type.TypeWrapperFactory.$;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import com.windowsazure.samples.Property;
import com.windowsazure.samples.table.AzureTableEntity;
import com.windowsazure.samples.table.AzureTableEntityCollection;

import jpa4azure.type.Key;
import jpa4azure.type.TypeWrapperFactory;
import jpa4azure.type.Utils;

@SuppressWarnings("rawtypes")
public class PersistanceCollection implements Collection {

	private ArrayList newdata;
	private AzureTableEntityCollection relationships;
	private boolean modified;
	private Object parent;
	private String propertyName;
	private AzureEntityManager aem;
	private Field field;
	
	public PersistanceCollection(Object parent, Field f, AzureEntityManager aem) {
		this.parent = parent;
		this.field = f;
		this.propertyName = f.getName();
		this.aem = aem;
	}
	
	private AzureTableEntityCollection data() {
		if (relationships == null) {
			relationships = aem.getRelationships(parent, propertyName);
		}
		return relationships;
	}
	
	protected Collection newdata() {
		if ( newdata == null)
			newdata = new ArrayList();
		return newdata;
	}
	
	protected Collection consumeUpdates() {
		Collection updates = newdata();
		newdata = null;
		return updates;
	}

	public boolean add(Object e) {
		modified = true;
		return newdata().add(e);
	}

	public boolean addAll(Collection c) {
		boolean success = true;
		for (Object o: c) {
			if (!add(o))
				success = false;
		}
		return success;		
	}


	public void clear() {
		Iterator<AzureTableEntity> it = data().iterator();
		while (it.hasNext()) {
			AzureTableEntity rel = it.next();
			aem.removeRelationEntity(rel.getPartitionKey(), rel.getRowKey(), parent);
			it.remove();
		}
	}

	public boolean contains(Object o) {
		for(AzureTableEntity rel : data()) {
			Key key = $(o).id(o);
			if (childPK(rel).equals(key.getPartition()) &&
				childRK(rel).equals(key.getRow()))
				return true;
		}
		return false;
	}

	public boolean containsAll(Collection c) {
		throw new UnsupportedOperationException();
	}

	public boolean equals(Object o) {
		return data().equals(o);
	}

	public boolean isEmpty() {
		return data().getEntities().isEmpty();
	}

	public Iterator iterator() {
		Class<?> genericType = Utils.genericType(field);
		return new LazyIterator(data(), aem, genericType, parent);
	}

	/**
	 * Object identity is handled by the id or key of the instance, not Object.equals() or 
	 * a = b.
	 */
	public boolean remove(Object o) {
		Key key = TypeWrapperFactory.$(o).id(o);
		Iterator<AzureTableEntity> it = data().iterator();
		while(it.hasNext()) {
			AzureTableEntity rel = it.next();
			if (childPK(rel).equals(key.getPartition()) &&
					childRK(rel).equals(key.getRow())) {
				aem.removeRelationEntity(rel.getPartitionKey(), rel.getRowKey(), parent);
				it.remove();
				return true;
			}				
		}
		return false;
	} 
	
	private String childPK(AzureTableEntity e) {
		for (Property p : e.getProperties()) {
			if (p.getName().equals("childPartitionKey"))
				return p.getRepresentation();
		}
		return null;
	}
	
	private String childRK(AzureTableEntity e) {
		for (Property p : e.getProperties()) {
			if (p.getName().equals("childRowKey"))
				return p.getRepresentation();
		}
		return null;
	}

	public boolean removeAll(Collection c) {
		throw new UnsupportedOperationException(); 
	}

	public boolean retainAll(Collection c) {
		throw new UnsupportedOperationException();
	}
	
	public int size() {
		return data().getEntities().size();
	}

	public Object[] toArray() {
		return data().getEntities().toArray();
	}

	public Object[] toArray(Object[] a) {
		throw new UnsupportedOperationException();
	}

	public boolean modified() {
		return modified;
	}

	public void save(AzureEntityManager aem) {
		for(Object o : consumeUpdates())
			aem.relate(parent, o, propertyName);	
		modified = false;
		relationships = null;
	}
	
}
